home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1999 #2 / Amiga Plus CD - 1999 - No. 2.iso / System-Boost / Workbench / ToolManager / Source / Prefs / listtree.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  18KB  |  627 lines

  1. /*
  2.  * listtree.c  V3.1
  3.  *
  4.  * ListTree class
  5.  *
  6.  * Copyright (C) 1990-98 Stefan Becker
  7.  *
  8.  * This source code is for educational purposes only. You may study it
  9.  * and copy ideas or algorithms from it for your own projects. It is
  10.  * not allowed to use any of the source codes (in full or in parts)
  11.  * in other programs. Especially it is not allowed to create variants
  12.  * of ToolManager or ToolManager-like programs from this source code.
  13.  *
  14.  */
  15.  
  16. #include "toolmanager.h"
  17.  
  18. /* Local data */
  19. static const char *TextNewGroup;
  20. static const char *TextNewObject;
  21.  
  22. /* ListTree class instance data */
  23. struct ListTreeClassData {
  24.  struct MUI_CustomClass *ltcd_Class;
  25.  ULONG                   ltcd_IsGroup;
  26. };
  27. #define TYPED_INST_DATA(cl, o) ((struct ListTreeClassData *) INST_DATA((cl), (o)))
  28. #define TREENODE(n)            ((struct MUIS_Listtree_TreeNode *) (n))
  29.  
  30. /* Destruct function  */
  31. #define DEBUGFUNCTION ListTreeClassDestruct
  32. __geta4 static void ListTreeClassDestruct(__a1 Object *obj)
  33. {
  34.  LISTTREE_LOG(LOG1(Object, "0x%08lx", obj))
  35.  
  36.  MUI_DisposeObject(obj);
  37. }
  38.  
  39. /* Display function  */
  40. #undef  DEBUGFUNCTION
  41. #define DEBUGFUNCTION ListTreeClassDisplay
  42. __geta4 static void ListTreeClassDisplay(
  43.                                         __a1 struct MUIS_Listtree_TreeNode *tn,
  44.                                         __a2 char **array)
  45. {
  46. #if DEBUG_VERY_NOISY
  47.  /* This just generates too much debug output... */
  48.  LISTTREE_LOG(LOG1(Object, "0x%08lx", tn->tn_User))
  49. #endif
  50.  
  51.  /* Get name of object */
  52.  GetAttr(TMA_Name, tn->tn_User, (ULONG *) array);
  53. }
  54.  
  55. /* Compare function  */
  56. #undef  DEBUGFUNCTION
  57. #define DEBUGFUNCTION ListTreeClassSortH
  58. __geta4 static LONG ListTreeClassSortH(__a1 struct MUIS_Listtree_TreeNode *tn1,
  59.                                        __a2 struct MUIS_Listtree_TreeNode *tn2)
  60. {
  61.  const char *s1, *s2;
  62.  
  63.  /* This just generates too much debug output... */
  64.  LISTTREE_LOG(LOG2(Entry, "Obj1 0x%08lx Obj2 0x%08lx", tn1->tn_User,
  65.                    tn2->tn_User))
  66.  
  67.  /* Get name of objects */
  68.  GetAttr(TMA_Name, tn1->tn_User, (ULONG *) &s1);
  69.  GetAttr(TMA_Name, tn2->tn_User, (ULONG *) &s2);
  70.  
  71.  /* Return result of string comparison */
  72.  return(strcmp(s2, s1));
  73. }
  74.  
  75. /* Hooks */
  76. static const struct Hook DestructHook = {
  77.  {NULL, NULL}, (void *) ListTreeClassDestruct, NULL, NULL
  78. };
  79. static const struct Hook DisplayHook = {
  80.  {NULL, NULL}, (void *) ListTreeClassDisplay, NULL, NULL
  81. };
  82. static const struct Hook SortHook = {
  83.  {NULL, NULL}, (void *) ListTreeClassSortH, NULL, NULL
  84. };
  85.  
  86. /* ListTree class method: OM_NEW */
  87. #undef  DEBUGFUNCTION
  88. #define DEBUGFUNCTION ListTreeClassNew
  89. static ULONG ListTreeClassNew(Class *cl, Object *obj, struct opSet *ops)
  90. {
  91.  LISTTREE_LOG((LOG1(Tags, "0x%08lx", ops->ops_AttrList),
  92.                PrintTagList(ops->ops_AttrList)))
  93.  
  94.  if (obj = (Object *) DoSuperNew(cl, obj,
  95.                                 InputListFrame,
  96.                                 MUIA_Listtree_DuplicateNodeName, FALSE,
  97.                                 MUIA_Listtree_DestructHook,      &DestructHook,
  98.                                 MUIA_Listtree_DisplayHook,       &DisplayHook,
  99.                                 MUIA_Listtree_SortHook,          &SortHook,
  100.                                 TAG_MORE, ops->ops_AttrList)) {
  101.  
  102.   /* Initialize instance data */
  103.   TYPED_INST_DATA(cl, obj)->ltcd_Class = (struct MUI_CustomClass *)
  104.                                 GetTagData(TMA_Class, NULL, ops->ops_AttrList);
  105.  
  106.   /* Double click on node names does not open it */
  107.   SetAttrs(obj, MUIA_Listtree_DoubleClick, MUIV_Listtree_DoubleClick_Off,
  108.                 TAG_DONE);
  109.  
  110.   /* Double click on node names/leaves starts editing */
  111.   DoMethod(obj, MUIM_Notify, MUIA_Listtree_DoubleClick, MUIV_EveryTime,
  112.             obj, 2, TMM_Selected, MUIV_TriggerValue);
  113.  }
  114.  
  115.  LISTTREE_LOG(LOG1(Result, "0x%08lx", obj))
  116.  
  117.  /* Return pointer to created object */
  118.  return((ULONG) obj);
  119. }
  120.  
  121. /* ListTree class method: OM_GET */
  122. #undef  DEBUGFUNCTION
  123. #define DEBUGFUNCTION ListTreeClassGet
  124. static ULONG ListTreeClassGet(Class *cl, Object *obj, struct opGet *opg)
  125. {
  126.  ULONG rc = FALSE;
  127.  
  128.  LISTTREE_LOG(LOG2(Attribute, "0x%08lx (%s)", opg->opg_AttrID,
  129.                    GetTagName(opg->opg_AttrID)))
  130.  
  131.  /* Which attribute is requested? */
  132.  switch(opg->opg_AttrID) {
  133.   case TMA_Active: {
  134.     struct MUIS_Listtree_TreeNode *tn;
  135.  
  136.     /* Get active entry */
  137.     GetAttr(MUIA_Listtree_Active, obj, (ULONG *) &tn);
  138.  
  139.     /* Is active entry an object? */
  140.     if ((tn->tn_Flags & TNF_LIST) == 0) {
  141.  
  142.      /* Return pointer to embedded object */
  143.      *opg->opg_Storage = (ULONG) tn->tn_User;
  144.  
  145.      /* Return TRUE to indicate that the attribute is implemented */
  146.      rc = TRUE;
  147.     }
  148.    }
  149.    break;
  150.  
  151.   default:
  152.    rc = DoSuperMethodA(cl, obj, (Msg) opg);
  153.    break;
  154.  }
  155.  
  156.  LISTTREE_LOG(LOG2(Result, "%ld value 0x%08lx", rc, *opg->opg_Storage))
  157.  
  158.  return(rc);
  159. }
  160.  
  161. /* ListTree class method: MUIM_DragQuery */
  162. #undef  DEBUGFUNCTION
  163. #define DEBUGFUNCTION ListTreeClassDragQuery
  164. static ULONG ListTreeClassDragQuery(Class *cl, Object *obj,
  165.                                     struct MUIP_DragQuery *mpdq)
  166. {
  167.  ULONG rc = MUIV_DragQuery_Refuse;
  168.  
  169. #if DEBUG_VERY_NOISY
  170.  /* This just generates too much debug output... */
  171.  LISTTREE_LOG(LOG2(Arguments, "Object 0x%08lx Source 0x%08lx", obj, mpdq->obj))
  172. #endif
  173.  
  174.  /* Is source our list? */
  175.  if (mpdq->obj == obj) {
  176.   struct MUIS_Listtree_TreeNode *active;
  177.  
  178.   /* Get active entry */
  179.   GetAttr(MUIA_Listtree_Active, obj, (ULONG *) &active);
  180.  
  181.   /* Dragging a group or an object? */
  182.   TYPED_INST_DATA(cl, obj)->ltcd_IsGroup = (active->tn_Flags & TNF_LIST) != 0;
  183.  
  184.   /* Call SuperClass */
  185.   rc = DoSuperMethodA(cl, obj, (Msg) mpdq);
  186.  }
  187.  
  188.  return(rc);
  189. }
  190.  
  191. /* ListTree class method: MUIM_DragReport */
  192. #undef  DEBUGFUNCTION
  193. #define DEBUGFUNCTION ListTreeClassDragReport
  194. static ULONG ListTreeClassDragReport(Class *cl, Object *obj,
  195.                                      struct MUIP_DragReport *mpdr)
  196. {
  197.  struct MUIS_Listtree_TestPos_Result tpr;
  198.  ULONG                               rc  = MUIV_DragReport_Continue;
  199.  
  200. #if DEBUG_VERY_NOISY
  201.  /* This just generates too much debug output... */
  202.  LISTTREE_LOG(LOG0(Entry))
  203. #endif
  204.  
  205. #if 1
  206.  /* This works, but the ListTree doesn't scroll... */
  207.  /* Test position */
  208.  if (DoMethod(obj, MUIM_Listtree_TestPos, mpdr->x, mpdr->y, &tpr)) {
  209.  
  210.   /* Update required? */
  211.   if (mpdr->update) {
  212.    BOOL IsGroup   = TYPED_INST_DATA(cl, obj)->ltcd_IsGroup;
  213.    BOOL GroupDrop;
  214.  
  215.    /* Yes, is current destination end of tree or above/below a group? */
  216.    GroupDrop = (TREENODE(tpr.tpr_TreeNode) == NULL) ||
  217.                ((TREENODE(tpr.tpr_TreeNode)->tn_Flags & TNF_LIST) &&
  218.                 ((tpr.tpr_Flags == MUIV_Listtree_TestPos_Result_Flags_Above) ||
  219.                  (tpr.tpr_Flags == MUIV_Listtree_TestPos_Result_Flags_Below)));
  220.  
  221.    /* Dragging a group and group drop or object and no group drop? */
  222.    if ((IsGroup && GroupDrop) || ((IsGroup == FALSE) && (GroupDrop == FALSE)))
  223.  
  224.     /* Yes, call SuperClass */
  225.     rc = DoSuperMethodA(cl, obj, (Msg) mpdr);
  226.  
  227.   /* No, just refresh */
  228.   } else rc = MUIV_DragReport_Refresh;
  229.  }
  230.  
  231. #else
  232.  /* This officially documented way doesn't work */
  233.  static LONG  old_entry = 0;
  234.  static ULONG old_flags = 0;
  235.  
  236.  /* Test position */
  237.  DoMethod(obj, MUIM_Listtree_TestPos, mpdr->x, mpdr->y, &tpr);
  238.  
  239.  /* Entry dragged over a new entry or flags changed? */
  240.  if ((tpr.tpr_ListEntry != old_entry) || (tpr.tpr_Flags != old_flags)) {
  241.  
  242.   /* Update required? */
  243.   if (mpdr->update) {
  244.    BOOL IsGroup   = TYPED_INST_DATA(cl, obj)->ltcd_IsGroup;
  245.    BOOL GroupDrop;
  246.  
  247.    /* Store new values */
  248.    old_entry = tpr.tpr_ListEntry;
  249.    old_flags = tpr.tpr_Flags;
  250.  
  251.    /* Yes, is current destination end of tree or above/below a group? */
  252.    GroupDrop = (TREENODE(tpr.tpr_TreeNode) == NULL) ||
  253.                ((TREENODE(tpr.tpr_TreeNode)->tn_Flags & TNF_LIST) &&
  254.                 ((tpr.tpr_Flags == MUIV_Listtree_TestPos_Result_Flags_Above) ||
  255.                  (tpr.tpr_Flags == MUIV_Listtree_TestPos_Result_Flags_Below)));
  256.  
  257.    /* Dragging a group and group drop or object and no group drop? */
  258.    if ((IsGroup && GroupDrop) || ((IsGroup == FALSE) && (GroupDrop == FALSE)))
  259.  
  260.     /* Yes, show drop mark */
  261.     DoMethod(obj, MUIM_Listtree_SetDropMark, old_entry, tpr.tpr_ListFlags);
  262.  
  263.    else
  264.  
  265.     /* No, don't show drop mark */
  266.     DoMethod(obj, MUIM_Listtree_SetDropMark, old_entry, MUIV_Listtree_SetDropMark_Values_None);
  267.  
  268.    /* Call SuperClass */
  269.    rc = DoSuperMethodA(cl, obj, (Msg) mpdr);
  270.  
  271.   } else
  272.  
  273.    /* No update required, just refresh */
  274.    rc = MUIV_DragReport_Refresh;
  275.  
  276.  } else
  277.  
  278.   /* No, just forward message to SuperClass */
  279.   rc = DoSuperMethodA(cl, obj, (Msg) mpdr);
  280. #endif
  281.  
  282.  return(rc);
  283. }
  284.  
  285. /* ListTree class method: TMM_NewGroup */
  286. #undef  DEBUGFUNCTION
  287. #define DEBUGFUNCTION ListTreeClassNewGroup
  288. static ULONG ListTreeClassNewGroup(Class *cl, Object *obj)
  289. {
  290.  struct MUIS_Listtree_TreeNode *rc    = NULL;
  291.  Object                        *group;
  292.  
  293.  LISTTREE_LOG(LOG0(Entry))
  294.  
  295.  /* Create new group object */
  296.  if (group = NewObject(GroupClass->mcc_Class, NULL, TMA_Name, TextNewGroup,
  297.                                                     TMA_List, obj,
  298.                                                     TAG_DONE)) {
  299.   struct MUIS_Listtree_TreeNode *active;
  300.  
  301.   LISTTREE_LOG(LOG1(Group, "0%08lx", group))
  302.  
  303.   /* Get active entry */
  304.   GetAttr(MUIA_Listtree_Active, obj, (ULONG *) &active);
  305.  
  306.   /* Active entry valid? */
  307.   if (active) {
  308.  
  309.    /* Is entry an object (= leaf)? */
  310.    if ((active->tn_Flags & TNF_LIST) == 0)
  311.  
  312.     /* Yes, get parent node */
  313.     active = TREENODE(DoMethod(obj, MUIM_Listtree_GetEntry, active,
  314.                                MUIV_Listtree_GetEntry_Position_Parent, 0));
  315.  
  316.   } else
  317.  
  318.    /* No, just add new group at the end of the list */
  319.    active = TREENODE(MUIV_Listtree_Insert_PrevNode_Tail);
  320.  
  321.   /* Insert group into tree */
  322.   if (rc = TREENODE(DoMethod(obj, MUIM_Listtree_Insert, TextNewGroup, group,
  323.                              MUIV_Listtree_Insert_ListNode_Root, active,
  324.                              TNF_LIST | TNF_OPEN))) {
  325.  
  326.    LISTTREE_LOG(LOG1(Group inserted, "0x%08lx", rc))
  327.  
  328.    /* Make it the active one */
  329.    SetAttrs(obj, MUIA_Listtree_Active, rc, TAG_DONE);
  330.  
  331.    /* Let the user edit the group name */
  332.    DoMethod(group, TMM_Edit, NULL);
  333.  
  334.   } else
  335.  
  336.    /* Couldn't insert group, delete it */
  337.    MUI_DisposeObject(group);
  338.  }
  339.  
  340.  LISTTREE_LOG(LOG1(Result, "0x%08lx", rc))
  341.  
  342.  /* Return pointer to TreeNode */
  343.  return((ULONG) rc);
  344. }
  345.  
  346. /* ListTree class method: TMM_NewObject */
  347. #undef  DEBUGFUNCTION
  348. #define DEBUGFUNCTION ListTreeClassNewObject
  349. static ULONG ListTreeClassNewObject(Class *cl, Object *obj)
  350. {
  351.  struct MUIS_Listtree_TreeNode *rc     = NULL;
  352.  Object                        *newobj;
  353.  
  354.  LISTTREE_LOG(LOG0(Entry))
  355.  
  356.  /* Create new object */ /* TEMPORARY! */
  357.  if (newobj = NewObject(TYPED_INST_DATA(cl, obj)->ltcd_Class->mcc_Class, NULL,
  358.                         TMA_Name, TextNewObject,
  359.                         TMA_List, obj,
  360.                         TAG_DONE)) {
  361.   struct MUIS_Listtree_TreeNode *active = TREENODE(
  362.                                            MUIV_Listtree_Insert_PrevNode_Tail);
  363.   struct MUIS_Listtree_TreeNode *list;
  364.  
  365.   LISTTREE_LOG(LOG1(Object, "0%08lx", newobj))
  366.  
  367.   /* Get active entry */
  368.   GetAttr(MUIA_Listtree_Active, obj, (ULONG *) &list);
  369.  
  370.   /* Active entry valid? */
  371.   if (list) {
  372.  
  373.    /* Yes, is entry an object (= leaf)? */
  374.    if ((list->tn_Flags & TNF_LIST) == 0) {
  375.  
  376.     /* Yes, append new object after active node */
  377.     active = list;
  378.  
  379.     /* Get parent node */
  380.     list = TREENODE(DoMethod(obj, MUIM_Listtree_GetEntry, list,
  381.                              MUIV_Listtree_GetEntry_Position_Parent, 0));
  382.    }
  383.   } else
  384.  
  385.    /* No, get last entry */
  386.    if ((list = TREENODE(DoMethod(obj, MUIM_Listtree_GetEntry,
  387.                                  MUIV_Listtree_GetEntry_ListNode_Root,
  388.                                  MUIV_Listtree_GetEntry_Position_Tail, 0)))
  389.         == NULL)
  390.  
  391.     /* No groups! Create one */
  392.     list = TREENODE(DoMethod(obj, TMM_NewGroup));
  393.  
  394.   LISTTREE_LOG(LOG1(List, "0%08lx", list))
  395.  
  396.   /* Insert group into tree */
  397.   if (list && (rc = TREENODE(DoMethod(obj, MUIM_Listtree_Insert, TextNewObject,
  398.                                        newobj, list, active, 0)))) {
  399.  
  400.    LISTTREE_LOG(LOG1(Object inserted, "0x%08lx", rc))
  401.  
  402.    /* Open group */
  403.    DoMethod(obj, MUIM_Listtree_Open, MUIV_Listtree_Open_ListNode_Root, list,
  404.             0);
  405.  
  406.    /* Make it the active one */
  407.    SetAttrs(obj, MUIA_Listtree_Active, rc, TAG_DONE);
  408.  
  409.    /* Let the user edit the object */
  410.    DoMethod(newobj, TMM_Edit, NULL);
  411.  
  412.   } else
  413.  
  414.    /* Couldn't insert object, delete it */
  415.    MUI_DisposeObject(newobj);
  416.  }
  417.  
  418.  LISTTREE_LOG(LOG1(Result, "0x%08lx", rc))
  419.  
  420.  /* Return pointer to TreeNode */
  421.  return((ULONG) rc);
  422. }
  423.  
  424. /* ListTree class method: TMM_Sort */
  425. #undef  DEBUGFUNCTION
  426. #define DEBUGFUNCTION ListTreeClassSort
  427. static ULONG ListTreeClassSort(Class *cl, Object *obj)
  428. {
  429.  struct MUIS_Listtree_TreeNode *tn;
  430.  
  431.  LISTTREE_LOG(LOG0(Entry))
  432.  
  433.  /* Get active node */
  434.  GetAttr(MUIA_Listtree_Active, obj, (ULONG *) &tn);
  435.  
  436.  /* Active node found? */
  437.  if (tn)
  438.  
  439.   /* List node? */
  440.   if (tn->tn_Flags & TNF_LIST) {
  441.  
  442.    /* Yes, list closed? Sort root */
  443.    if ((tn->tn_Flags & TNF_OPEN) == 0) tn = MUIV_Listtree_Sort_ListNode_Root;
  444.  
  445.   } else
  446.  
  447.    /* No, leaf node. Get parent node */
  448.    tn = TREENODE(DoMethod(obj, MUIM_Listtree_GetEntry, tn,
  449.                           MUIV_Listtree_GetEntry_Position_Parent, 0));
  450.  
  451.  else
  452.  
  453.   /* No, sort root list */
  454.   tn = MUIV_Listtree_Sort_ListNode_Root;
  455.  
  456.  /* Call sort method */
  457.  DoMethod(obj, MUIM_Listtree_Sort, tn, 0);
  458.  
  459.  return(0);
  460. }
  461.  
  462. /* ListTree class method: TMM_Update */
  463. #undef  DEBUGFUNCTION
  464. #define DEBUGFUNCTION ListTreeClassUpdate
  465. static ULONG ListTreeClassUpdate(Class *cl, Object *obj,
  466.                                  struct TMP_Update *tmpu)
  467. {
  468.  LISTTREE_LOG(LOG2(Entry, "Object 0x%08lx Type %ld", tmpu->tmpu_Entry,
  469.                    tmpu->tmpu_Type))
  470.  
  471.  /* Tell entry to finish */
  472.  DoMethod(tmpu->tmpu_Entry, TMM_Finish, tmpu->tmpu_Type);
  473.  
  474.  /* Redraw list */
  475.  DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
  476.  
  477.  /* Return 1 to indicate that the method is implemented */
  478.  return(1);
  479. }
  480.  
  481. /* ListTree class method dispatcher */
  482. #undef  DEBUGFUNCTION
  483. #define DEBUGFUNCTION ListTreeClassDispatcher
  484. __geta4 static ULONG ListTreeClassDispatcher(__a0 Class *cl, __a2 Object *obj,
  485.                                              __a1 Msg msg)
  486. {
  487.  ULONG rc;
  488.  
  489. #if DEBUG_VERY_NOISY
  490.  /* This just generates too much debug output... */
  491.  LISTTREE_LOG(LOG3(Arguments, "Class 0x%08lx Object 0x%08lx Msg 0x%08lx",
  492.                    cl, obj, msg))
  493. #endif
  494.  
  495.  switch(msg->MethodID) {
  496.   /* BOOPSI methods */
  497.   case OM_NEW:
  498.    rc = ListTreeClassNew(cl, obj, (struct opSet *) msg);
  499.    break;
  500.  
  501.   case OM_GET:
  502.    rc = ListTreeClassGet(cl, obj, (struct opGet *) msg);
  503.    break;
  504.  
  505.   /* MUI methods */
  506.   case MUIM_DragQuery:
  507.    rc = ListTreeClassDragQuery(cl, obj, (struct MUIP_DragQuery *) msg);
  508.    break;
  509.  
  510.   case MUIM_DragReport:
  511.    rc = ListTreeClassDragReport(cl, obj, (struct MUIP_DragReport *) msg);
  512.    break;
  513.  
  514.   /* TM methods */
  515.   case TMM_NewGroup:
  516.    rc = ListTreeClassNewGroup(cl, obj);
  517.    break;
  518.  
  519.   case TMM_NewObject:
  520.    rc = ListTreeClassNewObject(cl, obj);
  521.    break;
  522.  
  523.   case TMM_Sort:
  524.    rc = ListTreeClassSort(cl, obj);
  525.    break;
  526.  
  527.   case TMM_Selected:
  528.    /* Translate it to the TMM_Edit method for the attached object */
  529.    rc = DoMethod(((struct TMP_Selected *) msg)->tmps_Entry->tn_User,
  530.                  TMM_Edit, NULL);
  531.    break;
  532.  
  533.   case TMM_Update:
  534.    rc = ListTreeClassUpdate(cl, obj, (struct TMP_Update *) msg);
  535.    break;
  536.  
  537.   /* Unknown method -> delegate to SuperClass */
  538.   default:
  539.    rc = DoSuperMethodA(cl, obj, msg);
  540.    break;
  541.  }
  542.  
  543.  return(rc);
  544. }
  545.  
  546. /* Create ListTree class */
  547. #undef  DEBUGFUNCTION
  548. #define DEBUGFUNCTION CreateListTreeClass
  549. struct MUI_CustomClass *CreateListTreeClass(void)
  550. {
  551.  struct MUI_CustomClass *rc;
  552.  
  553.  /* Create class */
  554.  if (rc = MUI_CreateCustomClass(NULL, MUIC_Listtree, NULL,
  555.                                 sizeof(struct ListTreeClassData),
  556.                                 ListTreeClassDispatcher)) {
  557.  
  558.   /* Localize strings */
  559.   TextNewGroup  = TranslateString(LOCALE_TEXT_LISTTREE_NEW_GROUP_STR,
  560.                                   LOCALE_TEXT_LISTTREE_NEW_GROUP);
  561.   TextNewObject = TranslateString(LOCALE_TEXT_LISTTREE_NEW_OBJECT_STR,
  562.                                   LOCALE_TEXT_LISTTREE_NEW_OBJECT);
  563.  }
  564.  
  565.  LISTTREE_LOG(LOG1(Result, "0x%08lx", rc))
  566.  
  567.  return(rc);
  568. }
  569.  
  570. /* Search object with specified ID in list and attach to it */
  571. #undef  DEBUGFUNCTION
  572. #define DEBUGFUNCTION AttachObject
  573. struct AttachData *AttachObject(Object *list, Object *obj, ULONG id)
  574. {
  575.  struct MUIS_Listtree_TreeNode *group;
  576.  struct AttachData             *rc    = NULL;
  577.  
  578.  /* Get first group in list */
  579.  if (group = TREENODE(DoMethod(list, MUIM_Listtree_GetEntry,
  580.                                MUIV_Listtree_GetEntry_ListNode_Root,
  581.                                MUIV_Listtree_GetEntry_Position_Head,
  582.                                0))) {
  583.   ULONG objid;
  584.  
  585.   /* For each group in the list */
  586.   do {
  587.    struct MUIS_Listtree_TreeNode *tn  = group;
  588.    struct MUIS_Listtree_TreeNode *pos =
  589.                                 TREENODE(MUIV_Listtree_GetEntry_Position_Head);
  590.  
  591.    LISTTREE_LOG(LOG1(Next group, "0x%08lx", group))
  592.  
  593.    /* For each object in group */
  594.    while (tn = TREENODE(DoMethod(list, MUIM_Listtree_GetEntry, tn, pos, 0))) {
  595.  
  596.     CONFIG_LOG(LOG1(Next object, "0x%08lx", tn->tn_User))
  597.  
  598.     /* Get object ID */
  599.     GetAttr(TMA_ID, tn->tn_User, &objid);
  600.  
  601.     /* Object found? */
  602.     if (objid == id) {
  603.  
  604.      /* Yes, attach object */
  605.      rc = (struct AttachData *) DoMethod(tn->tn_User, TMM_Attach, obj);
  606.  
  607.      /* Leave loop */
  608.      break;
  609.     }
  610.  
  611.     /* Search for next object on the same level */
  612.     pos = TREENODE(MUIV_Listtree_GetEntry_Position_Next);
  613.    }
  614.  
  615.   /* Get next group */
  616.   } while ((rc == NULL) &&
  617.            (group = TREENODE(DoMethod(list, MUIM_Listtree_GetEntry,
  618.                                       group,
  619.                                       MUIV_Listtree_GetEntry_Position_Next,
  620.                                       0))));
  621.  }
  622.  
  623.  LISTTREE_LOG(LOG1(Result, "0x%08lx", rc))
  624.  
  625.  return(rc);
  626. }
  627.